home *** CD-ROM | disk | FTP | other *** search
- /*==========================================================================
-
- SlimApp.c
-
- This file contains functions to determine if the running application
- is a fat application and if so, to strip the unneeded code from the
- application.
-
- Add this file to your project and include "SlimApp.h" in the file(s)
- that call the stripping routines.
-
- Copyright 1995 by Blake Ward.
-
- Permission is granted for unrestricted use, provided the author
- is credited.
-
- ==========================================================================*/
-
- #include "SlimApp.h"
-
-
-
-
- /*============================================================================
- MISC STRUCTURES
- ============================================================================*/
-
- // This should have been defined by the UniversalHeaders for us, but I couldn't
- // find it. Getting at the long version message is actually a pain since the
- // strings are packed and the short version string is of variable length.
- typedef struct versResource {
- Byte versionNum1;
- Byte versionNum2;
- Byte release;
- Byte stage;
- short int regionCode;
- Str255 versionNumber; // Packed string, so next field has to be computed
- //Str255 versionMessage;
- } VersResource, **VersHandle;
-
-
-
-
- /*============================================================================
- INTERNAL PROTOTYPES (Functions used only within this file)
- ============================================================================*/
-
- OSErr StripUnneededCode(short int appResFork, short int appVRefNum,
- short int appDirID, StringPtr appFileName);
-
- void RenameSlimApplication(short int vRefNum, short int dirID, StringPtr name);
-
- void ForceFinderToUpdateFolder(short int vRefNum, short int dirID);
-
-
-
-
-
- /*============================================================================
- Has68KPowerPCCode:
-
- Returns:
- 'k68KApplication' if this application contains only 68K code
- 'kPowerPCApplication' if it contains only PowerPC code
- 'kFatBinaryApplication' if it contains both.
-
- This function assumes a simple model of 68k/PowerPC applications:
- - all of the PowerPC code is stored in the data fork
- - if there's PPC code, there will be at least one 'cfrg' resource
- - if there's 68K code, then our replacement stub will still be stored
- under a different resource type (stripping the 68K code moves it over
- to replace the applications regular 68K code resources).
-
- If you are using the Code Fragment Manager with 68K code, then you'll have
- to make this function a little more sophisticated.
-
- ============================================================================*/
- OSErr
- Has68KPowerPCCode(void)
- {
- OSErr err;
- short int currResFork, applicationResourceFork;
- Boolean is68KApp, isPowerPCApp;
-
- // If they didn't set this up for us, then we can't do much of anything
- applicationResourceFork = GetApplicationResourceFork();
-
- // Keep track of the current resource fork so that we can restore everything
- // to its previous state when we're done
- currResFork = CurResFile();
-
- // Look for the code and cfrg resources in the application's resource fork.
- UseResFile(applicationResourceFork);
-
- // First, see if our replacement 68K stub code resources are still stored
- // under a different resource type. .
- if (Count1Resources(kStubCODEType) == 2)
- is68KApp = true;
- else is68KApp = false;
-
- if ((err = ResError()) != noErr) {
- UseResFile(currResFork);
- return err;
- }
-
- // Also see if there are any 'cfrg' resources in the application
- if (Count1Resources('cfrg') > 0)
- isPowerPCApp = true;
- else isPowerPCApp = false;
-
- err = ResError();
- UseResFile(currResFork);
- if (err != noErr)
- return err;
-
- // Return the appropriate constant.
- if (is68KApp && isPowerPCApp)
- return kFatBinaryApplication;
- else if (isPowerPCApp)
- return kPowerPCApplication;
- else return k68KApplication;
-
- }
-
-
-
-
- /*============================================================================
- SafeToStrip:
-
- This function attempts to figure out whether it will be safe/possible to
- strip unneeded code from the application file at this time.
-
- Returns false if:
- the application file is locked
- the application file came from a remote server volume
- the application file is on a locked volume
-
- If the file isn't currently shared by any other users, it would be possible
- to strip it even if it resides on a remote server, but since it's a shared
- copy, that would probably only cause problems later when another user with
- a different machine tried to use it.
-
- Note that this routine allows the application to be stripped if it's on
- a local volume and that volume is currently shared using FileSharing.
- FileSharing won't let two users run the application at the same time, so
- we're safe from stripping it out from under another user. Generally a
- user that happens to have volume/directory containing the application
- will be sharing it so that others can copy files, not run applications,
- so this seems like a reasonable situation to allow.
-
- ============================================================================*/
- Boolean
- SafeToStrip(void)
- {
- OSErr err;
- short int applicationResourceFork;
-
- FCBPBRec fcbParams;
- Str63 appFileName;
-
- HParamBlockRec params;
- CInfoPBRec pb;
- GetVolParmsInfoBuffer volParms;
-
- // If they didn't set this up for us, then we can't do much of anything
- applicationResourceFork = GetApplicationResourceFork();
-
- // Build a parameter block for an FCB info request.
- fcbParams.ioCompletion = nil;
- fcbParams.ioNamePtr = appFileName;
- fcbParams.ioFCBIndx = 0;
- fcbParams.ioRefNum = applicationResourceFork;
-
- // We shouldn't get an error retrieving the info. If we do, there's
- // probably something seriously wrong, so the last thing we want to
- // do is say that it's safe to try to strip the application.
- err = PBGetFCBInfo(&fcbParams, false);
- if (err != noErr)
- return false;
-
- // First, check to see if the volume that contains the application is
- // currently locked. If so, we won't be able to change the application.
- // We get the volume's vRefNum from the values returned by the FCB call.
- params.volumeParam.ioCompletion = nil;
- params.volumeParam.ioVRefNum = fcbParams.ioFCBVRefNum;
- params.volumeParam.ioVolIndex = 0;
- params.volumeParam.ioNamePtr = nil;
- err = PBHGetVInfo(¶ms, false);
-
- // Check the volume hardware and software locked bits
- if (err != noErr ||
- (params.volumeParam.ioVAtrb & 0x0080) != 0 ||
- (params.volumeParam.ioVAtrb & 0x8000) != 0)
- return false;
-
- // Is the file itself locked? The application doesn't start out locked,
- // so this would only happen if the user locked the file. In any case,
- // if they did then we should honor it.
- pb.hFileInfo.ioNamePtr = appFileName;
- pb.hFileInfo.ioVRefNum = fcbParams.ioFCBVRefNum;
- pb.hFileInfo.ioDirID = fcbParams.ioFCBParID;
- pb.hFileInfo.ioFDirIndex = 0;
- err = PBGetCatInfoSync(&pb);
-
- // Check file locked bit
- if (err != noErr || (pb.hFileInfo.ioFlAttrib & 0x01) != 0)
- return false;
-
- // Get some general volume information to help us figure out whether we're
- // running from a local volume or from a server.
- params.ioParam.ioCompletion = nil;
- params.ioParam.ioVRefNum = fcbParams.ioFCBVRefNum;
- params.ioParam.ioNamePtr = nil;
- params.ioParam.ioBuffer = (Ptr)&volParms;
- params.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
- err = PBHGetVolParms(¶ms, false);
- if (err != noErr)
- return false;
-
- // If it's a local volume, then there won't be any server address
- if (volParms.vMServerAdr == 0)
- return true;
-
- return false;
-
- }
-
-
-
-
- /*============================================================================
- StripFatApplication:
- If running on a PowerPC, this function strips all the 68K code resources.
- If running on a 68K, it strips out the PowerPC code stored in the data
- fork.
-
- This function relies on the trick that we conditionally compile code for
- it depending on whether we're generating the code for a 68K or PowerPC
- processor. For a fat binary, this file will get compiled twice and the
- two results combined. If we're executing the code generated for one
- processor, we can be sure that the code for the other processor isn't
- currently executing (and isn't needed) and we can safely delete it.
-
- This function assumes a simple model of 68k/PowerPC applications:
- - all of the PowerPC code is stored in the data fork
- - if there's PPC code, there will be at least one 'cfrg' resource
- - if there's 68K code, then our replacement stub will still be stored
- under a different resource type (stripping the 68K code moves it over
- to replace the applications regular 68K code resources).
-
- ============================================================================*/
- OSErr
- StripFatApplication(void)
- {
-
- OSErr err;
- short int currResFork, applicationResourceFork;
-
- FCBPBRec fcbParams;
- Str63 appFileName;
-
- // If they didn't set this up for us, then we can't do much of anything
- applicationResourceFork = GetApplicationResourceFork();
-
- // Keep track of the current resource fork so that we can restore everything
- // to its previous state when we're done
- currResFork = CurResFile();
-
- // We're also going to need to the file itself before we're done. We can
- // get it from the resource fork refNum without having to depend on the
- // Process Manager
- fcbParams.ioCompletion = nil;
- fcbParams.ioNamePtr = appFileName;
- fcbParams.ioFCBIndx = 0;
- fcbParams.ioRefNum = applicationResourceFork;
-
- // We shouldn't get an error retrieving the info. If we do, there's
- // probably something seriously wrong and we're not going to be able
- // to strip the application.
- err = PBGetFCBInfo(&fcbParams, false);
- if (err != noErr)
- return err;
-
- // Look for the code and cfrg resources in the application's resource fork.
- UseResFile(applicationResourceFork);
-
- err = StripUnneededCode(applicationResourceFork, fcbParams.ioFCBVRefNum, fcbParams.ioFCBParID, appFileName);
-
- UseResFile(currResFork);
-
- // Provided we successfully stripped the unneeded code, we want to try to
- // change the application's name and it's long version string so that
- // the user can tell months from now which version he/she has.
- if (err == noErr)
- RenameSlimApplication(fcbParams.ioFCBVRefNum, fcbParams.ioFCBParID, appFileName);
-
- return err;
-
- }
-
-
-
- #ifdef powerc // The following version of this function will only be compiled
- // into the PowerPC version of the application
-
-
- /*============================================================================
- StripUnneededCode: (Strip Unneeded 68K Code)
-
- This function relies on the trick that we can conditionally compile code
- depending on whether we're generating the code for a 68K or PowerPC
- processor. This version of StripUnneededCode will only be compiled into
- the PowerPC version of the application. Therefore, if this code is
- actually executing, we can be absolutely sure that it's safe to remove
- the 68K version of the application.
-
- This function assumes that all of the 68K code is stored in 'CODE'
- resources in the application's resource fork. It removes all of the
- 'CODE' resources and (if there is one) the 'DATA' resource that contains
- initialization values for 68K global variables.
-
- In order to leave the application so that it will still run long enough
- to warn the user if ever moved to a 68K machine, this function inserts a
- tiny 68K application of its own. This application does nothing but
- initialize the toolbox, put up a warning dialog, and then quit. You
- should edit the dialog resource and customize it for your application
- and company name.
-
- If you also have fat resources for WDEF's, CDEF's, etc. and you want
- to strip them too, then you'll have to handle that yourself.
-
- ============================================================================*/
- OSErr
- StripUnneededCode(short int appResFork, short int /*appVRefNum*/, short int /*appDirID*/,
- StringPtr /*appFileName*/)
- {
-
- OSErr err;
- short int n;
- Handle resourceHandle;
-
- // First, see how many 'code' resources are in the application
- n = Count1Resources('CODE');
- if ((err = ResError()) != noErr)
- return err;
-
- // We don't actually want to read these resources into memory, we just want to
- // get a reference to them so that we can remove them from the resource fork.
- SetResLoad(false);
-
- // Delete all of the 'CODE' resources.
- for (; n > 0; n--) {
- resourceHandle = Get1IndResource('CODE', 1);
- if ((err = ResError()) != noErr || resourceHandle == nil) {
- SetResLoad(true);
- return err;
- }
-
- // The code resources start out protected, so we have to clear the
- // protected flag before they can be removed
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
-
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
-
- // Do the same for the DATA resource if it exists
- resourceHandle = Get1Resource('DATA', 0);
- if ((err = ResError()) == noErr && resourceHandle) {
- // In case it's protected, clear that flag first
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
-
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
-
- // We're finished deleting from the resource fork, so we want to make sure that
- // this flag gets reset. Forgetting to reset this would soon result in a crash.
- SetResLoad(true);
-
- // OK, now we want to move our tiny 68K stub into place so that this application
- // will still run long enough to warn the user if ever moved to a 68K machine
- // It consists of two code resources and a new DATA resource that we stored
- // under different resource types in the application.
- resourceHandle = Get1Resource(kStubCODEType, kStubCodeID);
- if ((err = ResError()) != noErr || resourceHandle == nil)
- return err;
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- AddResource(resourceHandle,'CODE',0,"\p");
- if ((err = ResError()) != noErr)
- return err;
- WriteResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- ReleaseResource(resourceHandle);
-
- // Move the second code resource
- resourceHandle = Get1Resource(kStubCODEType, kStubCodeID + 1);
- if ((err = ResError()) != noErr || resourceHandle == nil)
- return err;
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- AddResource(resourceHandle,'CODE',1,"\p");
- if ((err = ResError()) != noErr)
- return err;
- WriteResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- ReleaseResource(resourceHandle);
-
- // Move our DATA resource that goes with the code resources we just moved
- resourceHandle = Get1Resource(kStubDATAType, kStubDataID);
- if ((err = ResError()) != noErr || resourceHandle == nil)
- return err;
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- AddResource(resourceHandle,'DATA',0,"\p");
- if ((err = ResError()) != noErr)
- return err;
- WriteResource(resourceHandle);
- if ((err = ResError()) != noErr)
- return err;
- ReleaseResource(resourceHandle);
-
- // Write all of the changes
- UpdateResFile(appResFork);
- if ((err = ResError()) != noErr)
- return err;
-
- return noErr;
-
- }
-
-
- #else // The following version gets compiled into the 68K version
- // of the application.
-
-
- /*============================================================================
- StripUnneededCode: (Strip PowerPC Code)
-
- This function relies on the trick that we can conditionally compile code
- depending on whether we're generating the code for a 68K or PowerPC
- processor. This version of StripUnneededCode will only be compiled into
- the 680x0 version of the application. Therefore, if this code is
- actually executing, we can be absolutely sure that it's safe to remove
- the PowerPC version of the application.
-
- This function assumes that all of the PowerPC code is stored in the
- application's data fork.
-
- It also assumes that all of the 'cfrg' resources in the resource fork are
- part of the PowerPC version of the application and can safely be removed
- with the data fork. If you're also using the Code Fragment Manager with
- some 68K code (probably a very unusual situation), then you'll have to
- modify this function to be more selective about which 'cfrg' resources
- it removes.
-
- ============================================================================*/
- OSErr
- StripUnneededCode(short int appResFork, short int appVRefNum, short int appDirID,
- StringPtr appFileName)
- {
-
- OSErr err;
- short int n, refNum;
- Handle resourceHandle;
-
- // First, remove any 'cfrg' resources in the application resource fork
- // If we don't get rid of these and someone runs the application on a
- // PowerPC, the finder will think there's native PowerPC code available
- // and won't emulate the 68K version.
- n = Count1Resources('cfrg');
- if ((err = ResError()) != noErr)
- return err;
-
- // We don't actually want to read these resources into memory, we just want to
- // get a reference to them so that we can remove them from the resource fork.
- SetResLoad(false);
-
- // Delete all of the 'cfrg' resources.
- for (; n > 0; n--) {
- resourceHandle = Get1IndResource('cfrg', n);
- if ((err = ResError()) == noErr && resourceHandle) {
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
- else {
- SetResLoad(true);
- return err;
- }
- }
-
- // Since we've just stripped the PowerPC version of the application, we know that
- // we'll never be able to strip the 68K version, so there's no need to keep
- // around the stub code. Therefore, we'll make the app a little smaller by
- // removing it too.
- resourceHandle = Get1Resource(kStubCODEType, kStubCodeID);
- if ((err = ResError()) == noErr && resourceHandle) {
- // In case it's protected, clear that flag first
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
- resourceHandle = Get1Resource(kStubCODEType, kStubCodeID + 1);
- if ((err = ResError()) == noErr && resourceHandle) {
- // In case it's protected, clear that flag first
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
- resourceHandle = Get1Resource(kStubDATAType, kStubDataID);
- if ((err = ResError()) == noErr && resourceHandle) {
- // In case it's protected, clear that flag first
- SetResAttrs(resourceHandle, GetResAttrs(resourceHandle) & ~resProtected);
- RemoveResource(resourceHandle);
- if ((err = ResError()) != noErr) {
- SetResLoad(true);
- return err;
- }
- DisposeHandle(resourceHandle);
- }
-
- // We're finished with the resource fork, so we want to make sure that this flag
- // gets reset. Forgetting to reset this would soon result in a crash.
- SetResLoad(true);
-
- // Write the changes
- UpdateResFile(appResFork);
- if ((err = ResError()) != noErr)
- return(err);
-
- // Now we have to remove the actual PowerPC code. This function assumes
- // that it's all stored in the data fork of the application. If your
- // application has fat resources (e.g. WDEF functions) and you also want
- // to strip the PowerPC code from them, you'll have to handle that
- // specially here.
-
- // Open the data fork (which contains all of the PPC code)
- err = HOpen(appVRefNum, appDirID, appFileName, fsRdWrPerm, &refNum);
- if (err != noErr)
- return(err);
-
- // And eliminate the whole data fork
- err = SetEOF(refNum, 0);
- if (err != noErr)
- return err;
-
- err = FSClose(refNum);
- if (err != noErr)
- return err;
-
- return noErr;
-
- }
-
- #endif
-
-
-
-
- /*============================================================================
- RenameSlimApplication:
-
- This routine updates the name of the application and its version string.
-
- If the application's file name ends the kFatBinaryFileName string
- which is currently set to " (Fat)" in the STR# resource, then this routine
- renames the file to remove it.
-
- If the long version string for the application contains the string
- kFatBinaryVersionName, this routine replaces it with kPowerPCVersionName or
- k68KVersionName as appropriate.
-
- ============================================================================*/
- void
- RenameSlimApplication(short int vRefNum, short int dirID, StringPtr name)
- {
- Str63 newName;
- Str255 searchStr, replaceStr;
- Handle theStrHandle;
-
- // Regardless of which version of the code we're stripping, if the filename
- // ends in " (Fat)" then we should remove that:
-
- // Get the string we're searching for
- GetIndString(searchStr, kSlimAppStrings, kFatBinaryFileName);
-
- // Get the same number of characters off the end of the filename
- BlockMoveData(&(name[name[0] - searchStr[0] + 1]), &replaceStr[1], searchStr[0]);
- replaceStr[0] = searchStr[0];
-
- // If they match, truncate the filename
- if (EqualString(searchStr, replaceStr, false, false)) {
- BlockMoveData(name, newName, name[0] + 1);
- newName[0] -= searchStr[0];
-
- // Change the fileName (and ignore any error code since there's not
- // much we can do about it anyway)
- HRename(vRefNum, dirID, name, newName);
- }
-
- // Get the current long version string
- GetLongVersion(searchStr);
- PtrToHand(&searchStr[1], &theStrHandle, searchStr[0]);
-
- // Get the "fat binary" string we want to search for
- GetIndString(searchStr, kSlimAppStrings, kFatBinaryVersionName);
-
- // Get the right replacement string depending on which version we've now got
- #ifdef powerc
- GetIndString(replaceStr, kSlimAppStrings, kPowerMacVersionName);
- #else
- GetIndString(replaceStr, kSlimAppStrings, k68KVersionName);
- #endif
-
- // ReplaceText() would be better since it handles any script system, but we
- // want to remain compatible with System-6.
- Munger(theStrHandle, 0L, &searchStr[1], searchStr[0], &replaceStr[1], replaceStr[0]);
-
- searchStr[0] = GetHandleSize(theStrHandle);
- BlockMoveData(*theStrHandle, &searchStr[1], searchStr[0]);
- SetLongVersion(searchStr);
-
- // If the application's name & info are currently visible in an open Finder window
- // this will update the displayed info in the next few seconds. Otherwise, it won't
- // update until the folder is closed and reopened. Note that the long version string
- // that is displayed by the Finder's Get Info command unfortunately won't actually
- // update until the user has quit the application.
- ForceFinderToUpdateFolder(vRefNum, dirID);
-
- }
-
-
-
-
- /*============================================================================
- ForceFinderToUpdateFolder:
-
- This routine is adapted from a tip in MacTech Magazine.
- It just gets the Finder to notice that we've changed some characteristics
- of the file and update its folder if it happens to be currently displayed.
-
- ============================================================================*/
- void
- ForceFinderToUpdateFolder(short int vRefNum, short int dirID)
- {
- CInfoPBRec tempPB;
-
- tempPB.dirInfo.ioNamePtr = nil;
- tempPB.dirInfo.ioVRefNum = vRefNum;
- tempPB.dirInfo.ioFDirIndex = -1;
- tempPB.dirInfo.ioDrDirID = dirID;
-
- if (PBGetCatInfoSync(&tempPB) == noErr) {
- tempPB.dirInfo.ioDrMdDat = LMGetTime();
- tempPB.dirInfo.ioDrDirID = dirID;
- PBSetCatInfoSync(&tempPB);
- }
- }
-
-
-
-
-
- /*============================================================================
- ApplicationResourceFork:
-
- This function just returns the refNum for this application's (currently
- open) resource fork. The function GetAppParms() returns exactly what we
- need, but it isn't defined by the Universal Headers if you're compiling
- for a PowerPC. Therefore, we actually need two versions of this function
- if we want to remain System-6 compatible.
-
- For the PowerPC, they must be running System 7 or later, so we can be
- certain that the Process Manager is available, and we can use it to get
- an FSSpec for the application. When we open the resource fork, we
- actually just get the already open refNum, so there's
- no need to close it later.
-
- ============================================================================*/
- short int
- GetApplicationResourceFork(void)
- {
-
- #ifdef powerc
-
- ProcessSerialNumber psn;
- ProcessInfoRec pInfo;
- FSSpec theAppFile;
-
- GetCurrentProcess(&psn);
- pInfo.processInfoLength = sizeof(ProcessInfoRec);
- pInfo.processName = nil;
- pInfo.processAppSpec = &theAppFile;
- GetProcessInformation(&psn, &pInfo);
-
- // This should just return the refNum of the already open application
- // resource fork.
- return(FSpOpenResFile(&theAppFile, fsCurPerm));
-
- #else
-
- Str255 appName;
- Handle appFiles;
- short int appResFork;
-
- GetAppParms(appName, &appResFork, &appFiles);
-
- return(appResFork);
-
- #endif
- }
-
-
-
-
-
- /*============================================================================
- GetLongVersion:
-
- This routine retrieves the long version string from the 'vers' resource.
-
- ============================================================================*/
- void
- GetLongVersion(StringPtr versMsg)
- {
- VersHandle versionInfo;
- StringPtr versionStr;
- short int curResFork;
-
- curResFork = CurResFile();
- UseResFile(GetApplicationResourceFork());
-
- // Retrieve the resource
- versionInfo = (VersHandle)Get1Resource('vers', 1);
- UseResFile(curResFork);
- if (versionInfo == nil) {
- versMsg[0] = '\0';
- return;
- }
-
- // Take into account the variable length of the short version string that preceeds
- // the long version string
- versionStr = (StringPtr)&((*versionInfo)->versionNumber) + (*versionInfo)->versionNumber[0] + 1;
-
- // Extract the long version string
- BlockMoveData(versionStr, versMsg, versionStr[0] + 1);
-
- // And we're done
- ReleaseResource((Handle)versionInfo);
-
- }
-
-
-
-
-
- /*============================================================================
- GetShortVersion:
-
- This routine retrieves the short version string from 'vers' resource .
-
- ============================================================================*/
- void
- GetShortVersion(StringPtr versMsg)
- {
- VersHandle versionInfo;
- short int curResFork;
-
- curResFork = CurResFile();
- UseResFile(GetApplicationResourceFork());
-
- // Retrieve the resource
- versionInfo = (VersHandle)Get1Resource('vers', 1);
- UseResFile(curResFork);
- if (versionInfo == nil) {
- versMsg[0] = '\0';
- return;
- }
-
- // Extract the long version string
- BlockMoveData((*versionInfo)->versionNumber, versMsg, (*versionInfo)->versionNumber[0] + 1);
-
- // And we're done
- ReleaseResource((Handle)versionInfo);
- }
-
-
-
-
- /*============================================================================
- SetLongVersion:
-
- This routine changes the long version string in 'vers' resource #1 to the
- given string.
-
- ============================================================================*/
- OSErr
- SetLongVersion(StringPtr versionStr)
- {
- OSErr err;
- VersHandle versionInfo;
- StringPtr currentVersion;
- short int curResFork;
-
- // Make sure that we're using the application's 'vers' string
- curResFork = CurResFile();
- UseResFile(GetApplicationResourceFork());
-
- // Retrieve the resource
- versionInfo = (VersHandle)Get1Resource('vers', 1);
- if (versionInfo == nil) {
- err = ResError();
- UseResFile(curResFork);
- return err;
- }
-
- // Get a pointer to the current long version string, taking into account the
- // variable length of the short version string that preceeds it
- currentVersion = (StringPtr)&((*versionInfo)->versionNumber) +
- (*versionInfo)->versionNumber[0] + 1;
-
- // Make enough room in the resource for the new version string (if we couldn't get enough, give up)
- SetHandleSize((Handle)versionInfo,
- GetHandleSize((Handle)versionInfo) - currentVersion[0] + versionStr[0]);
- if (MemError()) {
- UseResFile(curResFork);
- return MemError();
- }
-
- // The resource handle might have moved, so get a pointer to the current long
- // version string again
- currentVersion = (StringPtr)&((*versionInfo)->versionNumber) +
- (*versionInfo)->versionNumber[0] + 1;
-
- // And replace it with the new string
- BlockMoveData(versionStr, currentVersion, versionStr[0] + 1);
- ChangedResource((Handle)versionInfo);
- WriteResource((Handle)versionInfo);
- ReleaseResource((Handle)versionInfo);
-
- UseResFile(curResFork);
-
- return noErr;
- }
-
-
-
-
-